/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.assertj.core.api;

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toCollection;
import static org.assertj.core.error.ShouldBeASCII.shouldBeASCII;
import static org.assertj.core.error.ShouldBeAlphabetic.shouldBeAlphabetic;
import static org.assertj.core.error.ShouldBeAlphanumeric.shouldBeAlphanumeric;
import static org.assertj.core.error.ShouldBeBlank.shouldBeBlank;
import static org.assertj.core.error.ShouldBeHexadecimal.shouldBeHexadecimal;
import static org.assertj.core.error.ShouldBePrintable.shouldBePrintable;
import static org.assertj.core.error.ShouldBeVisible.shouldBeVisible;
import static org.assertj.core.error.ShouldContainCharSequence.shouldContainIgnoringWhitespaces;
import static org.assertj.core.error.ShouldContainOneOrMoreWhitespaces.shouldContainOneOrMoreWhitespaces;
import static org.assertj.core.error.ShouldContainOnlyWhitespaces.shouldContainOnlyWhitespaces;
import static org.assertj.core.error.ShouldNotBeBlank.shouldNotBeBlank;
import static org.assertj.core.error.ShouldNotContainAnyWhitespaces.shouldNotContainAnyWhitespaces;
import static org.assertj.core.error.ShouldNotContainOnlyWhitespaces.shouldNotContainOnlyWhitespaces;
import static org.assertj.core.error.ShouldNotEndWithWhitespaces.shouldNotEndWithWhitespaces;
import static org.assertj.core.error.ShouldNotStartWithWhitespaces.shouldNotStartWithWhitespaces;
import static org.assertj.core.internal.Strings.doCommonCheckForCharSequence;
import static org.assertj.core.internal.Strings.removeAllWhitespaces;
import static org.assertj.core.util.IterableUtil.toArray;

import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.assertj.core.annotation.CheckReturnValue;
import org.assertj.core.api.comparisonstrategy.ComparatorBasedComparisonStrategy;
import org.assertj.core.internal.Strings;

/**
 * Base class for all implementations of assertions for {@code CharSequence}s.
 *
 * @param <SELF>   the "self" type of this assertion class. Please read &quot;<a href="http://bit.ly/1IZIRcY"
 *                 target="_blank">Emulating 'self types' using Java Generics to simplify fluent API implementation</a>&quot;
 *                 for more details.
 * @param <ACTUAL> the type of the "actual" value.
 * @author Yvonne Wang
 * @author David DIDIER
 * @author Alex Ruiz
 * @author Joel Costigliola
 * @author Mikhail Mazursky
 * @author Nicolas Francois
 * @author Daniel Weber
 */
public abstract class AbstractCharSequenceAssert<SELF extends AbstractCharSequenceAssert<SELF, ACTUAL>, ACTUAL extends CharSequence>
    extends AbstractAssertWithComparator<SELF, ACTUAL> implements EnumerableAssert<SELF, Character> {

  // TODO reduce the visibility of the fields annotated with @VisibleForTesting
  Strings strings = Strings.instance();

  protected AbstractCharSequenceAssert(ACTUAL actual, Class<?> selfType) {
    super(actual, selfType);
  }

  /**
   * Verifies that the actual {@code CharSequence} is empty, i.e., it has a length of 0, or is {@code null}.
   * <p>
   * If you do not want to accept a {@code null} value, use
   * {@link org.assertj.core.api.AbstractCharSequenceAssert#isEmpty()} instead.
   * <p>
   * Both of these assertions succeeds:
   * <pre><code class='java'> String emptyString = &quot;&quot;
   * assertThat(emptyString).isNullOrEmpty();
   *
   * String nullString = null;
   * assertThat(nullString).isNullOrEmpty();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat(&quot;a&quot;).isNullOrEmpty();
   * assertThat(&quot;   &quot;).isNullOrEmpty();</code></pre>
   *
   * @throws AssertionError if the actual {@code CharSequence} has a non-zero length.
   */
  @Override
  public void isNullOrEmpty() {
    strings.assertNullOrEmpty(info, actual);
  }

  /**
   * Verifies that the actual {@code CharSequence} is empty, i.e., it has a length of 0 and is not {@code null}.
   * <p>
   * If you want to accept a {@code null} value as well as a 0 length, use
   * {@link org.assertj.core.api.AbstractCharSequenceAssert#isNullOrEmpty()} instead.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'> String emptyString = &quot;&quot;
   * assertThat(emptyString).isEmpty();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> String nullString = null;
   * assertThat(nullString).isEmpty();
   * assertThat(&quot;a&quot;).isEmpty();
   * assertThat(&quot;   &quot;).isEmpty();</code></pre>
   *
   * @throws AssertionError if the actual {@code CharSequence} has a non-zero length or is null.
   */
  @Override
  public void isEmpty() {
    strings.assertEmpty(info, actual);
  }

  /**
   * Verifies that the actual {@code CharSequence} is not empty, i.e., is not {@code null} and has a length of 1 or
   * more.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'> String bookName = &quot;A Game of Thrones&quot;
   * assertThat(bookName).isNotEmpty();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> String emptyString = &quot;&quot;
   * assertThat(emptyString).isNotEmpty();
   *
   * String nullString = null;
   * assertThat(nullString).isNotEmpty();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is empty (has a length of 0).
   */
  @Override
  public SELF isNotEmpty() {
    strings.assertNotEmpty(info, actual);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is blank, i.e. is {@code null}, empty or consists of one or more
   * whitespace characters (according to {@link Character#isWhitespace(char)}).
   * <p>
   * The definition of this method has changed, the old behaviour is now under {@link #containsOnlyWhitespaces()}.
   * <p>
   * These assertions succeed:
   * <pre><code class='java'> assertThat(" ").isBlank();
   * assertThat("").isBlank();
   * assertThat("    ").isBlank();
   * String nullString = null;
   * assertThat(nullString).isBlank();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat("a").isBlank();
   * assertThat(" b").isBlank();
   * assertThat(" c ").isBlank();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not blank.
   * @since 2.6.0 / 3.6.0
   */
  public SELF isBlank() {
    assertBlank(actual);
    return myself;
  }

  private void assertBlank(CharSequence actual) {
    if (!isBlank(actual)) throw assertionError(shouldBeBlank(actual));
  }

  /**
   * Verifies that the actual {@code CharSequence} is:
   * <ul>
   *   <li><b>not</b> {@code null}</li>
   *   <li><b>not</b> empty</li>
   *   <li>contains at least one non-whitespace character (according to {@link Character#isWhitespace(char)})</li>
   * </ul>
   * <p>
   * The definition of this method has changed, the old behaviour is now under {@link #doesNotContainOnlyWhitespaces()}.
   * <p>
   * These assertions succeed:
   * <pre><code class='java'> assertThat("a").isNotBlank();
   * assertThat(" b").isNotBlank();
   * assertThat(" c ").isNotBlank();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat(" ").isNotBlank();
   * assertThat("").isNotBlank();
   * assertThat("    ").isNotBlank();
   * String nullString = null;
   * assertThat(nullString).isNotBlank();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is blank.
   * @since 2.6.0 / 3.6.0
   */
  public SELF isNotBlank() {
    assertNotBlank(actual);
    return myself;
  }

  private void assertNotBlank(CharSequence actual) {
    if (isBlank(actual)) throw assertionError(shouldNotBeBlank(actual));
  }

  /**
   * Verifies that the actual {@code CharSequence} contains one or more whitespace characters (according to
   * {@link Character#isWhitespace(char)}).
   * <p>
   * These assertions succeed:
   * <pre><code class='java'> assertThat(" ").containsWhitespaces();
   * assertThat("a b").containsWhitespaces();
   * assertThat(" c ").containsWhitespaces();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat("").containsWhitespaces();
   * assertThat("a").containsWhitespaces();
   * String nullString = null;
   * assertThat(nullString).containsWhitespaces();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} does not contain any whitespace characters.
   * @since 3.11.0
   */
  public SELF containsWhitespaces() {
    assertContainsWhitespaces(actual);
    return myself;
  }

  private void assertContainsWhitespaces(CharSequence actual) {
    if (!containsWhitespaces(actual)) throw assertionError(shouldContainOneOrMoreWhitespaces(actual));
  }

  /**
   * Verifies that the actual {@code CharSequence} consists of one or more whitespace characters (according to
   * {@link Character#isWhitespace(char)}).
   * <p>
   * These assertions succeed:
   * <pre><code class='java'> assertThat(" ").containsOnlyWhitespaces();
   * assertThat("    ").containsOnlyWhitespaces();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat("a").containsOnlyWhitespaces();
   * assertThat("").containsOnlyWhitespaces();
   * assertThat(" b").containsOnlyWhitespaces();
   * assertThat(" c ").containsOnlyWhitespaces();
   *
   * String nullString = null;
   * assertThat(nullString).containsOnlyWhitespaces();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not blank.
   * @since 2.9.0 / 3.9.0
   */
  public SELF containsOnlyWhitespaces() {
    assertContainsOnlyWhitespaces(actual);
    return myself;
  }

  private void assertContainsOnlyWhitespaces(CharSequence actual) {
    if (!containsOnlyWhitespaces(actual)) throw assertionError(shouldContainOnlyWhitespaces(actual));
  }

  /**
   * Verifies that the actual {@code CharSequence} is either {@code null}, empty or does not contain any whitespace characters (according to {@link Character#isWhitespace(char)}).
   * <p>
   * These assertions succeed:
   * <pre><code class='java'> assertThat("a").doesNotContainAnyWhitespaces();
   * assertThat("").doesNotContainAnyWhitespaces();
   * assertThat("ab").doesNotContainAnyWhitespaces();
   *
   * String nullString = null;
   * assertThat(nullString).doesNotContainAnyWhitespaces();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat(" ").doesNotContainAnyWhitespaces();
   * assertThat(" a").doesNotContainAnyWhitespaces();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} contains one or more whitespace characters.
   * @since 3.11.0
   */
  public SELF doesNotContainAnyWhitespaces() {
    assertDoesNotContainAnyWhitespaces(actual);
    return myself;
  }

  private void assertDoesNotContainAnyWhitespaces(CharSequence actual) {
    if (containsWhitespaces(actual)) throw assertionError(shouldNotContainAnyWhitespaces(actual));
  }

  /**
   * Verifies that the actual {@code CharSequence} is either:
   * <ul>
   *   <li>{@code null}</li>
   *   <li>empty</li>
   *   <li>contains at least one non-whitespace character (according to {@link Character#isWhitespace(char)}).</li>
   * </ul>
   * <p>
   * The main difference with {@link #isNotBlank()} is that it accepts null or empty {@code CharSequence}.
   * <p>
   * These assertions succeed:
   * <pre><code class='java'> assertThat("a").doesNotContainOnlyWhitespaces();
   * assertThat("").doesNotContainOnlyWhitespaces();
   * assertThat(" b").doesNotContainOnlyWhitespaces();
   * assertThat(" c ").doesNotContainOnlyWhitespaces();
   * String nullString = null;
   * assertThat(nullString).doesNotContainOnlyWhitespaces();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat(" ").doesNotContainOnlyWhitespaces();
   * assertThat("    ").doesNotContainOnlyWhitespaces();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is blank.
   * @since 2.9.0 / 3.9.0
   */
  public SELF doesNotContainOnlyWhitespaces() {
    assertDoesNotContainOnlyWhitespaces(actual);
    return myself;
  }

  private void assertDoesNotContainOnlyWhitespaces(CharSequence actual) {
    if (containsOnlyWhitespaces(actual)) throw assertionError(shouldNotContainOnlyWhitespaces(actual));
  }

  /**
   * Verifies that the actual {@code CharSequence} has the expected length using the {@code length()} method.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'> String bookName = &quot;A Game of Thrones&quot;
   * assertThat(bookName).hasSize(17);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'> String bookName = &quot;A Clash of Kings&quot;
   * assertThat(bookName).hasSize(4);</code></pre>
   *
   * @param expected the expected length of the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual length is not equal to the expected length.
   */
  @Override
  public SELF hasSize(int expected) {
    strings.assertHasSize(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has a length less than the given value using the {@code length()} method.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'>assertThat(&quot;abc&quot;).hasSizeLessThan(4);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'>assertThat(&quot;abc&quot;).hasSizeLessThan(3);</code></pre>
   *
   * @param expected the expected maximum length of the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual length is equal or greater than the expected length.
   * @since 3.12.0
   */
  @Override
  public SELF hasSizeLessThan(int expected) {
    strings.assertHasSizeLessThan(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has a length less than or equal to the given value using the {@code length()}  method.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'>assertThat(&quot;abc&quot;).hasSizeLessThanOrEqualTo(3);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'>assertThat(&quot;abc&quot;).hasSizeLessThanOrEqualTo(2);</code></pre>
   *
   * @param expected the expected maximum length of the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual length is greater than the expected length.
   * @since 3.12.0
   */
  @Override
  public SELF hasSizeLessThanOrEqualTo(int expected) {
    strings.assertHasSizeLessThanOrEqualTo(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has a length greater than the given value using the {@code length()}  method.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'>assertThat(&quot;abcs&quot;).hasSizeGreaterThan(2);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'>assertThat(&quot;abc&quot;).hasSizeGreaterThan(3);</code></pre>
   *
   * @param expected the expected minimum length of the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual length is equal or less than the expected length.
   * @since 3.12.0
   */
  @Override
  public SELF hasSizeGreaterThan(int expected) {
    strings.assertHasSizeGreaterThan(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has a length greater than or equal to the given value using the {@code length()}  method.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'>assertThat(&quot;abc&quot;).hasSizeGreaterThanOrEqualTo(3);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'>assertThat(&quot;abc&quot;).hasSizeGreaterThanOrEqualTo(3);</code></pre>
   *
   * @param expected the expected minimum length of the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual length is less than the expected length.
   * @since 3.12.0
   */
  @Override
  public SELF hasSizeGreaterThanOrEqualTo(int expected) {
    strings.assertHasSizeGreaterThanOrEqualTo(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has length between the given boundaries (inclusive)
   * using the {@code length()} method.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'> String bookName = &quot;A Game of Thrones&quot;
   * assertThat(bookName).hasSizeBetween(5, 25);
   * assertThat(bookName).hasSizeBetween(16, 17);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'> String bookName = &quot;A Clash of Kings&quot;
   * assertThat(bookName).hasSizeBetween(2, 5);</code></pre>
   *
   * @param lowerBoundary  the lower boundary compared to which actual length should be greater than or equal to.
   * @param higherBoundary the higher boundary compared to which actual length should be less than or equal to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual length is not between the boundaries.
   * @since 3.12.0
   */
  @Override
  public SELF hasSizeBetween(int lowerBoundary, int higherBoundary) {
    strings.assertHasSizeBetween(info, actual, lowerBoundary, higherBoundary);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has the expected line count.
   * <p>
   * A line is considered to be <i>terminated</i> by any one of a line feed ({@code '\n'}), a carriage return ({@code '\r'}),
   * or a carriage return followed immediately by a linefeed (see {@link java.io.LineNumberReader}).
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'> String multiLine = &quot;First line\n&quot; +
   *                    &quot;Last line&quot;;
   * assertThat(multiLine).hasLineCount(2);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'> String bookName = &quot;A Clash of Kings&quot;;
   * assertThat(bookName).hasLineCount(3);</code></pre>
   *
   * @param expectedLineCount the expected line count of the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual line count is not equal to the expected one.
   */
  public SELF hasLineCount(int expectedLineCount) {
    strings.assertHasLineCount(info, actual, expectedLineCount);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has a length that's the same as the length of the given
   * {@code CharSequence}.
   * <p>
   * Examples :
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;C-3PO&quot;).hasSameSizeAs(&quot;R2-D2&quot;);
   *
   * // assertion will fail as actual and expected sizes differ
   * assertThat(&quot;C-3PO&quot;).hasSameSizeAs(&quot;B1 battle droid&quot;);</code></pre>
   *
   * @param other the given {@code CharSequence} to be used for size comparison.
   * @return {@code this} assertion object.
   * @throws AssertionError       if the actual {@code CharSequence} has a length that's different from the length of the
   *                              given {@code CharSequence}.
   * @throws NullPointerException if the given {@code CharSequence} is {@code null}.
   */
  public SELF hasSameSizeAs(CharSequence other) {
    strings.assertHasSameSizeAs(info, actual, other);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has a length that's the same as the number of elements in the given
   * array.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;12&quot;).hasSameSizeAs(new char[] { 'a', 'b' });
   *
   * // assertion will fail
   * assertThat(&quot;12&quot;).hasSameSizeAs(new char[] { 'a', 'b', 'c' });</code></pre>
   *
   * @param other the given array to be used for size comparison.
   * @return {@code this} assertion object.
   * @throws AssertionError       if the actual {@code CharSequence} has a length that's different from the number of elements
   *                              in the array.
   * @throws NullPointerException if the given array is {@code null}.
   */
  @Override
  public SELF hasSameSizeAs(Object other) {
    strings.assertHasSameSizeAs(info, actual, other);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} has a length that's the same as the number of elements in the given
   * Iterable.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;abc&quot;).hasSameSizeAs(Arrays.asList(1, 2, 3));
   *
   * // assertion will fail
   * assertThat(&quot;ab&quot;).hasSameSizeAs(Arrays.asList(1, 2, 3));</code></pre>
   *
   * @param other the given {@code Iterable} to be used for size comparison.
   * @return {@code this} assertion object.
   * @throws AssertionError       if the actual {@code CharSequence} has a length that's different from the number of elements
   *                              in the {@code Iterable}.
   * @throws NullPointerException if the given {@code Iterable} is {@code null}.
   */
  @Override
  public SELF hasSameSizeAs(Iterable<?> other) {
    strings.assertHasSameSizeAs(info, actual, other);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is equal to the given one, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Gandalf the grey&quot;).isEqualToIgnoringCase(&quot;GaNdAlF tHe GREY&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).isEqualToIgnoringCase(&quot;Gandalf the grey&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Gandalf the grey&quot;).isEqualToIgnoringCase(&quot;Gandalf the white&quot;);</code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not equal to the given one.
   */
  public SELF isEqualToIgnoringCase(CharSequence expected) {
    strings.assertEqualsIgnoringCase(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is not equal to the given one, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat("Gandalf").isNotEqualToIgnoringCase("Hobbit");
   * assertThat("Gandalf").isNotEqualToIgnoringCase("HOBit");
   * assertThat((String) null).isNotEqualToIgnoringCase("Gandalf");
   * assertThat("Gandalf").isNotEqualToIgnoringCase(null);
   *
   * // assertions will fail
   * assertThat("Gandalf").isNotEqualToIgnoringCase("Gandalf");
   * assertThat("Gandalf").isNotEqualToIgnoringCase("GaNDalf");
   * assertThat((String) null).isNotEqualToIgnoringCase(null);</code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not equal to the given one.
   */
  public SELF isNotEqualToIgnoringCase(CharSequence expected) {
    strings.assertNotEqualsIgnoringCase(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains only digits. It fails if it contains non-digit
   * characters or is empty.
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'> assertThat("10").containsOnlyDigits();</code></pre>
   * <p>
   * Whereas these assertions fail:
   * <pre><code class='java'> assertThat("10$").containsOnlyDigits();
   * assertThat("").containsOnlyDigits();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} contains non-digit characters or is {@code null}.
   */
  public SELF containsOnlyDigits() {
    strings.assertContainsOnlyDigits(info, actual);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains the given sequence <b>only once</b>.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).containsOnlyOnce(&quot;do&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;Frodo&quot;).containsOnlyOnce(&quot;o&quot;);
   * assertThat(&quot;Frodo&quot;).containsOnlyOnce(&quot;y&quot;);</code></pre>
   *
   * @param sequence the sequence to search for.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} either does not contain the given one at all, or contains
   *                        it more than once.
   */
  public SELF containsOnlyOnce(CharSequence sequence) {
    strings.assertContainsOnlyOnce(info, actual, sequence);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains all the given values.
   * <p>
   * You can use one or several {@code CharSequence}s as in this example:
   * <pre><code class='java'> assertThat(&quot;Gandalf the grey&quot;).contains(&quot;alf&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).contains(&quot;alf&quot;, &quot;grey&quot;);</code></pre>
   *
   * @param values the values to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} does not contain all the given values.
   */
  public SELF contains(CharSequence... values) {
    strings.assertContains(info, actual, values);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains any of the given values.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion succeeds
   * assertThat(&quot;Gandalf the grey&quot;).containsAnyOf(&quot;grey&quot;, &quot;black&quot;);
   *
   * // assertion fails
   * assertThat(&quot;Gandalf the grey&quot;).containsAnyOf(&quot;white&quot;, &quot;black&quot;);</code></pre>
   *
   * @param values the values to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} does not contain any of the given values.
   * @since 3.21.0
   */
  public SELF containsAnyOf(CharSequence... values) {
    strings.assertContainsAnyOf(info, actual, values);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains all the {@code CharSequence}s of the given Iterable.
   * <p>
   * Examples:
   * <pre><code class='java'> assertThat(&quot;Gandalf the grey&quot;).contains(Arrays.asList(&quot;alf&quot;));
   * assertThat(&quot;Gandalf the grey&quot;).contains(Arrays.asList(&quot;alf&quot;, &quot;grey&quot;));</code></pre>
   *
   * @param values the values to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} does not contain all the given values.
   */
  public SELF contains(Iterable<? extends CharSequence> values) {
    strings.assertContains(info, actual, toArray(values, CharSequence.class));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains the given sequence of values <b>in the given order without any other values between them</b>.
   * <p>
   * <b>Breaking change since 2.9.0</b>: in previous versions this assertion behaved like {@link #containsSubsequence(CharSequence...) containsSubsequence}
   * and allowed other values between the sequence values.
   * <p>
   * Example:
   * <pre><code class='java'> String book = &quot;{ 'title':'A Game of Thrones', 'author':'George Martin'}&quot;;
   *
   * // this assertion succeeds
   * assertThat(book).containsSequence(&quot;'title'&quot;, &quot;:&quot;, &quot;'A Game of Thrones'&quot;);
   *
   * // this assertion will fail because there are values between the expected sequence (e.g &quot;'title':'&quot;)
   * assertThat(book).containsSequence(&quot;{&quot;, &quot;A Game of Thrones&quot;, &quot;George Martin&quot;, &quot;}&quot;);
   *
   * // this one fails as &quot;:&quot; must come after &quot;'title'&quot;
   * assertThat(book).containsSequence(&quot;:&quot;, &quot;'title'&quot;, &quot;'A Game of Thrones'&quot;);</code></pre>
   *
   * @param values the sequence of charSequence to look for, in order.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given sequence of charSequence is {@code null}.
   * @throws IllegalArgumentException if the given sequence of charSequence is empty.
   * @throws AssertionError           if the given {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the given {@code CharSequence} does not contain the given sequence of values in the given order without any other values between them.
   */
  public SELF containsSequence(CharSequence... values) {
    strings.assertContainsSequence(info, actual, values);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains all the values of the given Iterable <b>in the Iterable
   * iteration order without any other values between them</b>.
   * <p>
   * <b>Breaking change since 2.9.0</b>: in previous versions this assertion behaved like {@link #containsSubsequence(Iterable) containsSubsequence}
   * and allowed other values between the sequence values.
   * <p>
   * Example:
   * <pre><code class='java'> String book = &quot;{ 'title':'A Game of Thrones', 'author':'George Martin'}&quot;;
   *
   * // this assertion succeeds
   * assertThat(book).containsSequence(asList(&quot;'title'&quot;, &quot;:&quot;, &quot;'A Game of Thrones'&quot;));
   *
   * // this assertion will fail because there are values between the expected sequence (e.g &quot;'title':'&quot;)
   * assertThat(book).containsSequence(asList(&quot;{&quot;, &quot;A Game of Thrones&quot;, &quot;George Martin&quot;, &quot;}&quot;));
   *
   * // this one fails as &quot;author&quot; must come after &quot;A Game of Thrones&quot;
   * assertThat(book).containsSequence(asList(&quot;author&quot;, &quot;A Game of Thrones&quot;));</code></pre>
   *
   * @param values the sequence of charSequence to look for, in order.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given sequence of charSequence is {@code null}.
   * @throws IllegalArgumentException if the given sequence of charSequence is empty.
   * @throws AssertionError           if the given {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the given {@code CharSequence} does not contain the given sequence of values in the given order without any other charvalues between them.
   */
  public SELF containsSequence(Iterable<? extends CharSequence> values) {
    strings.assertContainsSequence(info, actual, toArray(values, CharSequence.class));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains all the given values <b>in the given order,
   * possibly with other values between them</b>.
   * <p>
   * Example:
   * <pre><code class='java'> String book = &quot;{ 'title':'A Game of Thrones', 'author':'George Martin'}&quot;;
   *
   * // this assertion succeeds
   * assertThat(book).containsSubsequence(&quot;'title'&quot;, &quot;:&quot;, &quot;'A Game of Thrones'&quot;);
   *
   * // these ones succeed even if there are values between the given values
   * assertThat(book).containsSubsequence(&quot;{&quot;, &quot;A Game of Thrones&quot;, &quot;George Martin&quot;, &quot;}&quot;);
   * assertThat(book).containsSubsequence(&quot;A&quot;, &quot;Game&quot;, &quot;of&quot;, &quot;George&quot;);
   *
   * // this one fails as &quot;author&quot; must come after &quot;A Game of Thrones&quot;
   * assertThat(book).containsSubsequence(&quot;{&quot;, &quot;author&quot;, &quot;A Game of Thrones&quot;, &quot;}&quot;);</code></pre>
   *
   * @param values the Strings to look for, in order.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given values is {@code null}.
   * @throws IllegalArgumentException if the given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} does not contain all the given values in the given order.
   */
  public SELF containsSubsequence(CharSequence... values) {
    strings.assertContainsSubsequence(info, actual, values);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains all the values of the given Iterable <b>in the Iterable
   * iteration order (possibly with other values between them)</b>.
   * <p>
   * Example:
   * <pre><code class='java'> String book = &quot;{ 'title':'A Game of Thrones', 'author':'George Martin'}&quot;;
   *
   * // this assertion succeeds
   * assertThat(book).containsSubsequence(asList(&quot;'title'&quot;, &quot;:&quot;, &quot;'A Game of Thrones'&quot;));
   *
   * // these ones succeed even if there are values between the given values
   * assertThat(book).containsSubsequence(asList(&quot;{&quot;, &quot;A Game of Thrones&quot;, &quot;George Martin&quot;, &quot;}&quot;));
   * assertThat(book).containsSubsequence(asList(&quot;A&quot;, &quot;Game&quot;, &quot;of&quot;, &quot;George&quot;));
   *
   * // but this one fails as &quot;author&quot; must come after &quot;A Game of Thrones&quot;
   * assertThat(book).containsSubsequence(asList(&quot;{&quot;, &quot;author&quot;, &quot;A Game of Thrones&quot;, &quot;}&quot;));</code></pre>
   *
   * @param values the Strings to look for, in order.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given values is {@code null}.
   * @throws IllegalArgumentException if the given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} does not contain all the given values in the given order.
   */
  public SELF containsSubsequence(Iterable<? extends CharSequence> values) {
    strings.assertContainsSubsequence(info, actual, toArray(values, CharSequence.class));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains the given sequence, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Gandalf the grey&quot;).containsIgnoringCase(&quot;gandalf&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Gandalf the grey&quot;).containsIgnoringCase(&quot;white&quot;);</code></pre>
   *
   * @param sequence the sequence to search for.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given sequence is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not contain the given one.
   */
  public SELF containsIgnoringCase(CharSequence sequence) {
    strings.assertContainsIgnoringCase(info, actual, sequence);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains all the given values, ignoring whitespace differences.
   * <p>
   * You can use one or several {@code CharSequence}s as in this example:
   * <pre><code class='java'> // assertions succeed:
   * assertThat(&quot;Gandalf the grey&quot;).containsIgnoringWhitespaces(&quot;alf&quot;)
   *                               .containsIgnoringWhitespaces(&quot;alf&quot;, &quot;grey&quot;)
   *                               .containsIgnoringWhitespaces(&quot;thegrey&quot;)
   *                               .containsIgnoringWhitespaces(&quot;thegr  ey&quot;)
   *                               .containsIgnoringWhitespaces(&quot;t h e g r\t\r\n ey&quot;);
   * // assertion fails:
   * assertThat(&quot;Gandalf the grey&quot;).containsIgnoringWhitespaces(&quot;alF&quot;)</code></pre>
   *
   * @param values the values to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} does not contain all the given values.
   */
  public SELF containsIgnoringWhitespaces(CharSequence... values) {
    assertContainsIgnoringWhitespaces(values);
    return myself;
  }

  private void assertContainsIgnoringWhitespaces(CharSequence... values) {
    doCommonCheckForCharSequence(info, actual, values);
    String actualWithoutWhitespace = removeAllWhitespaces(actual);
    Set<CharSequence> notFound = stream(values).map(Strings::removeAllWhitespaces)
                                               .filter(value -> !objects.getComparisonStrategy()
                                                                        .stringContains(actualWithoutWhitespace, value))
                                               .collect(toCollection(LinkedHashSet::new));
    if (notFound.isEmpty()) return;
    if (values.length == 1) {
      throw assertionError(shouldContainIgnoringWhitespaces(actual, values[0], objects.getComparisonStrategy()));
    }
    throw assertionError(shouldContainIgnoringWhitespaces(actual, values, notFound, objects.getComparisonStrategy()));
  }

  /**
   * Verifies that the actual {@code CharSequence} contains all the given values ignoring newline differences.
   * <p>
   * You can use one or several {@code CharSequence}s as in this example:
   *
   * <pre><code class='java'> // assertions succeed:
   * assertThat(&quot;Gandalf\nthe\ngrey&quot;).containsIgnoringNewlines(&quot;alf&quot;)
   *                                 .containsIgnoringNewlines(&quot;alf&quot;, &quot;grey&quot;)
   *                                 .containsIgnoringNewlines(&quot;thegrey&quot;)
   *                                 .containsIgnoringNewlines(&quot;thegr\ney&quot;)
   *                                 .containsIgnoringNewlines(&quot;t\nh\ne\ng\nr\ney&quot;);
   * // assertions fail:
   * assertThat(&quot;Gandalf\nthe\ngrey&quot;).containsIgnoringNewlines(&quot;alF&quot;)
   * assertThat(&quot;Gandalf\nthe\ngrey&quot;).containsIgnoringNewlines(&quot;t\nh\ne\ng\nr\t\r\ney&quot;)</code></pre>
   *
   * @param values the values to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} does not contain all the given values.
   */
  public SELF containsIgnoringNewlines(final CharSequence... values) {
    strings.assertContainsIgnoringNewlines(info, actual, values);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain any of the given values.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotContain(&quot;pippin&quot;)
   *                    .doesNotContain(&quot;fro&quot;, &quot;sam&quot;);
   *
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).doesNotContain(&quot;Fro&quot;, &quot;Gimli&quot;, &quot;Legolas&quot;);</code></pre>
   *
   * @param values the CharSequences to search for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} contains any of the given values.
   */
  public SELF doesNotContain(CharSequence... values) {
    strings.assertDoesNotContain(info, actual, values);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain any of the given Iterable.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotContain(Arrays.asList(&quot;&quot;))
   *                    .doesNotContain(Arrays.asList(&quot;fro&quot;, &quot;sam&quot;));
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).doesNotContain(Arrays.asList(&quot;Fro&quot;, &quot;Gimli&quot;, &quot;Legolas&quot;));</code></pre>
   *
   * @param values the CharSequences to search for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} contains any of the given values.
   */
  public SELF doesNotContain(Iterable<? extends CharSequence> values) {
    strings.assertDoesNotContain(info, actual, toArray(values, CharSequence.class));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain the given sequence;
   * a sequence is defined as a group of values arranged <b>in the given order without any other values
   * between them</b>.
   * <p>
   * Example:
   * <pre><code class='java'>{@literal  String book = "{ 'title':'A Game of Thrones', 'author':'George Martin' }";
   *
   * // assertions will pass
   * assertThat(book).doesNotContainSequence("A Game of ", "Shadows");
   * assertThat(book).doesNotContainSequence("A Game of ", "George Martin");
   *
   * // assertions will fail
   * assertThat(book).doesNotContainSequence("A Game of ", "Thrones");}</code></pre>
   *
   * @param sequence the sequence to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given values is {@code null}.
   * @throws IllegalArgumentException if the given values is empty.
   * @throws AssertionError           if the given {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the given {@code CharSequence} contains the given values in the given order
   *                                  without any characters between them.
   */
  public SELF doesNotContainSequence(CharSequence... sequence) {
    strings.assertDoesNotContainSequence(info, actual, sequence);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain the given sequence;
   * a sequence is defined as a group of values arranged <b>in the given order without any other values
   * between them</b>.
   * <p>
   * Example:
   * <pre><code class='java'>{@literal  String book = "{ 'title':'A Game of Thrones', 'author':'George Martin' }";
   *
   * // assertions will pass
   * assertThat(book).doesNotContainSequence(asList("A Game of ", "Shadows"));
   * assertThat(book).doesNotContainSequence(asList("A Game of ", "George Martin"));
   *
   * // assertions will fail
   * assertThat(book).doesNotContainSequence(asList("A Game of ", "Thrones"));}</code></pre>
   *
   * @param sequence the sequence to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given values is {@code null}.
   * @throws IllegalArgumentException if the given values is empty.
   * @throws AssertionError           if the given {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the given {@code CharSequence} contains the given values in the given order
   *                                  without any characters between them.
   */
  public SELF doesNotContainSequence(Iterable<? extends CharSequence> sequence) {
    strings.assertDoesNotContainSequence(info, actual, toArray(sequence, CharSequence.class));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain the given subsequence;
   * a subsequence is defined as a group of values arranged <b>in the given order, possibly with some values
   * between them</b>.
   * <p>
   * Example:
   * <pre><code class='java'>{@literal  String book = "{ 'title':'A Game of Thrones', 'author':'George Martin' }";
   *
   * // assertions will pass
   * assertThat(book).doesNotContainSubsequence("A Game of ", "Shadows");
   *
   * // assertions will fail
   * assertThat(book).doesNotContainSubsequence("A Game of ", "George Martin");
   * assertThat(book).doesNotContainSubsequence("A Game of ", "Thrones");}</code></pre>
   *
   * @param subsequence the subsequence to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given values is {@code null}.
   * @throws IllegalArgumentException if the given values is empty.
   * @throws AssertionError           if the given {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the given {@code CharSequence} contains the given values in the given order,
   *                                  possibly with other characters between them.
   */
  public SELF doesNotContainSubsequence(CharSequence... subsequence) {
    strings.assertDoesNotContainSubsequence(info, actual, subsequence);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain the given subsequence;
   * a subsequence is defined as a group of values arranged <b>in the given order, possibly with some values
   * between them</b>.
   * <p>
   * Example:
   * <pre><code class='java'>{@literal  String book = "{ 'title':'A Game of Thrones', 'author':'George Martin' }";
   *
   * // assertions will pass
   * assertThat(book).doesNotContainSubsequence(asList("A Game of ", "Shadows"));
   *
   * // assertions will fail
   * assertThat(book).doesNotContainSubsequence(asList("A Game of ", "George Martin"));
   * assertThat(book).doesNotContainSubsequence(asList("A Game of ", "Thrones"));}</code></pre>
   *
   * @param subsequence the subsequence to look for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given values is {@code null}.
   * @throws IllegalArgumentException if the given values is empty.
   * @throws AssertionError           if the given {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the given {@code CharSequence} contains the given values in the given order,
   *                                  possibly with other characters between them.
   */
  public SELF doesNotContainSubsequence(Iterable<? extends CharSequence> subsequence) {
    strings.assertDoesNotContainSubsequence(info, actual, toArray(subsequence, CharSequence.class));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain any of the given values, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Frodo&quot;).doesNotContainIgnoringCase(&quot;pippin&quot;)
   *                    .doesNotContainIgnoringCase(&quot;Merry&quot;, &quot;sam&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;Frodo&quot;).doesNotContainIgnoringCase(&quot;Fro&quot;, &quot;Gimli&quot;, &quot;Legolas&quot;);
   * assertThat(&quot;Frodo&quot;).doesNotContainIgnoringCase(&quot;fro&quot;); </code></pre>
   *
   * @param values the CharSequences to search for.
   * @return {@code this} assertion object.
   * @throws NullPointerException     if the given list of values is {@code null}.
   * @throws NullPointerException     if any one of the given values is {@code null}.
   * @throws IllegalArgumentException if the list of given values is empty.
   * @throws AssertionError           if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError           if the actual {@code CharSequence} contains any one of the given values, ignoring case considerations.
   * @since 3.17.0
   */
  public SELF doesNotContainIgnoringCase(CharSequence... values) {
    strings.assertDoesNotContainIgnoringCase(info, actual, values);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain the given regular expression.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotContainPattern(&quot;Fr.ud&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Freud&quot;).doesNotContainPattern(&quot;Fr.ud&quot;);</code></pre>
   *
   * @param pattern the regular expression to find in the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the given regular expression can be found in the actual {@code CharSequence}.
   * @since 2.7.0 / 3.7.0
   */
  public SELF doesNotContainPattern(CharSequence pattern) {
    strings.assertDoesNotContainPattern(info, actual, pattern);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not contain the given regular expression.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotContainPattern(Pattern.compile(&quot;Fr.ud&quot;));
   *
   * // assertion will fail
   * assertThat(&quot;Freud&quot;).doesNotContainPattern(Pattern.compile(&quot;Fr.ud&quot;));</code></pre>
   *
   * @param pattern the regular expression to find in the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the given regular expression can be found in the actual {@code CharSequence}.
   * @since 2.7.0 / 3.7.0
   */
  public SELF doesNotContainPattern(Pattern pattern) {
    strings.assertDoesNotContainPattern(info, actual, pattern);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} starts with the given prefix.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Frodo&quot;).startsWith(&quot;Fro&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).startsWith(&quot;Gandalf&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;Frodo&quot;).startsWith(&quot;fro&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).startsWith(&quot;grey&quot;);</code></pre>
   *
   * @param prefix the given prefix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given prefix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not start with the given prefix.
   */
  public SELF startsWith(CharSequence prefix) {
    strings.assertStartsWith(info, actual, prefix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} starts with the given prefix, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Gandalf the grey&quot;).startsWithIgnoringCase(&quot;Gandalf&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).startsWithIgnoringCase(&quot;gandalf&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Gandalf the grey&quot;).startsWithIgnoringCase(&quot;grey&quot;);</code></pre>
   *
   * @param prefix the given prefix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given prefix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not start with the given prefix, ignoring case.
   * @since 3.23.0
   */
  public SELF startsWithIgnoringCase(CharSequence prefix) {
    strings.assertStartsWithIgnoringCase(info, actual, prefix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not start with the given prefix.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Frodo&quot;).doesNotStartWith(&quot;fro&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).doesNotStartWith(&quot;grey&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;Gandalf the grey&quot;).doesNotStartWith(&quot;Gandalf&quot;);
   * assertThat(&quot;Frodo&quot;).doesNotStartWith(&quot;&quot;);</code></pre>
   *
   * @param prefix the given prefix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given prefix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} starts with the given prefix.
   */
  public SELF doesNotStartWith(CharSequence prefix) {
    strings.assertDoesNotStartWith(info, actual, prefix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not start with the given prefix, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Gandalf the grey&quot;).doesNotStartWithIgnoringCase(&quot;fro&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).doesNotStartWithIgnoringCase(&quot;grey&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;Gandalf the grey&quot;).doesNotStartWithIgnoringCase(&quot;Gandalf&quot;);
   * assertThat(&quot;Gandalf the grey&quot;).doesNotStartWithIgnoringCase(&quot;gandalf&quot;);</code></pre>
   *
   * @param prefix the given prefix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given prefix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} starts with the given prefix, ignoring case.
   * @since 3.23.0
   */
  public SELF doesNotStartWithIgnoringCase(CharSequence prefix) {
    strings.assertDoesNotStartWithIgnoringCase(info, actual, prefix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} ends with the given suffix, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).endsWith(&quot;do&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).endsWith(&quot;Fro&quot;);</code></pre>
   *
   * @param suffix the given suffix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given suffix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not end with the given suffix.
   */
  public SELF endsWith(CharSequence suffix) {
    strings.assertEndsWith(info, actual, suffix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} ends with the given suffix, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Frodo&quot;).endsWithIgnoringCase(&quot;do&quot;);
   * assertThat(&quot;Frodo&quot;).endsWithIgnoringCase(&quot;DO&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).endsWithIgnoringCase(&quot;Fro&quot;);</code></pre>
   *
   * @param suffix the given suffix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given suffix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not end with the given suffix, ignoring case.
   * @since 3.23.0
   */
  public SELF endsWithIgnoringCase(CharSequence suffix) {
    strings.assertEndsWithIgnoringCase(info, actual, suffix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not end with the given suffix.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotEndWith(&quot;Fro&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;Frodo&quot;).doesNotEndWith(&quot;do&quot;);
   * assertThat(&quot;Frodo&quot;).doesNotEndWith(&quot;&quot;);</code></pre>
   *
   * @param suffix the given suffix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given suffix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} ends with the given suffix.
   */
  public SELF doesNotEndWith(CharSequence suffix) {
    strings.assertDoesNotEndWith(info, actual, suffix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not end with the given suffix, ignoring case considerations.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotEndWithIgnoringCase(&quot;Fro&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;Frodo&quot;).doesNotEndWithIgnoringCase(&quot;do&quot;);
   * assertThat(&quot;Frodo&quot;).doesNotEndWithIgnoringCase(&quot;DO&quot;);</code></pre>
   *
   * @param suffix the given suffix.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given suffix is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} ends with the given suffix, ignoring case.
   * @since 3.23.0
   */
  public SELF doesNotEndWithIgnoringCase(CharSequence suffix) {
    strings.assertDoesNotEndWithIgnoringCase(info, actual, suffix);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} matches the given regular expression.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).matches(&quot;..o.o&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).matches(&quot;.*d&quot;);</code></pre>
   *
   * @param regex the regular expression to which the actual {@code CharSequence} is to be matched.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not match the given regular expression.
   */
  public SELF matches(CharSequence regex) {
    strings.assertMatches(info, actual, regex);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} matches the given regular expression pattern, then accepts the given
   * {@code Consumer<Matcher>} to do further verification on the matcher.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion succeeds:
   * assertThat(&quot;Frodo&quot;).matchesSatisfying(&quot;..(o.o)&quot;, matcher -&gt; assertThat(matcher.group(1)).isEqualTo(&quot;odo&quot;)); </code></pre>
   *
   * @param regex          the regular expression to which the actual {@code CharSequence} is to be matched.
   * @param matchSatisfies a consumer of the found match to do further verification
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not match the given regular expression.
   */
  public SELF matchesSatisfying(CharSequence regex, Consumer<Matcher> matchSatisfies) {
    return internalMatchesSatisfying(Pattern.compile(regex.toString()), matchSatisfies);
  }

  /**
   * Verifies that the actual {@code CharSequence} does not match the given regular expression.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotMatch(&quot;.*d&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).doesNotMatch(&quot;..o.o&quot;);</code></pre>
   *
   * @param regex the regular expression to which the actual {@code CharSequence} is to be matched.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} matches the given regular expression.
   */
  public SELF doesNotMatch(CharSequence regex) {
    strings.assertDoesNotMatch(info, actual, regex);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} matches the given regular expression pattern.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).matches(Pattern.compile(&quot;..o.o&quot;));
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).matches(Pattern.compile(&quot;.*d&quot;));</code></pre>
   *
   * @param pattern the regular expression to which the actual {@code CharSequence} is to be matched.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not match the given regular expression.
   */
  public SELF matches(Pattern pattern) {
    strings.assertMatches(info, actual, pattern);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} matches the given regular expression pattern, then accepts the given
   * {@code Consumer<Matcher>} to do further verification on the matcher.
   * <p>
   * Example:
   * <pre><code class='java'> Pattern pattern = Pattern.compile("..(o.o)");
   *
   * // assertion succeeds:
   * assertThat(&quot;Frodo&quot;).matchesSatisfying(pattern, matcher -&gt; assertThat(matcher.group(1)).isEqualTo(&quot;odo&quot;)); </code></pre>
   *
   * @param pattern        the regular expression to which the actual {@code CharSequence} is to be matched.
   * @param matchSatisfies a consumer of the found match to do further verification
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not match the given regular expression.
   */
  public SELF matchesSatisfying(Pattern pattern, Consumer<Matcher> matchSatisfies) {
    return internalMatchesSatisfying(pattern, matchSatisfies);
  }

  // internal method to avoid double proxying if one assertion calls another one
  private SELF internalMatchesSatisfying(Pattern pattern, Consumer<Matcher> matchSatisfies) {
    Matcher matcher = pattern.matcher(actual);
    strings.assertMatches(info, actual, matcher);
    matchSatisfies.accept(matcher);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not match the given regular expression pattern.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).doesNotMatch(Pattern.compile(&quot;.*d&quot;));
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).doesNotMatch(Pattern.compile(&quot;..o.o&quot;));</code></pre>
   *
   * @param pattern the regular expression to which the actual {@code CharSequence} is to be matched.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} does not match the given regular expression.
   */
  public SELF doesNotMatch(Pattern pattern) {
    strings.assertDoesNotMatch(info, actual, pattern);
    return myself;
  }

  /**
   * Do not use this method.
   *
   * @throws UnsupportedOperationException if this method is called.
   * @deprecated Custom element Comparator is not supported for CharSequence comparison.
   */
  @Override
  @Deprecated
  public final SELF usingElementComparator(Comparator<? super Character> customComparator) {
    throw new UnsupportedOperationException("custom element Comparator is not supported for CharSequence comparison");
  }

  /**
   * Do not use this method.
   *
   * @throws UnsupportedOperationException if this method is called.
   * @deprecated Custom element Comparator is not supported for CharSequence comparison.
   */
  @Override
  @Deprecated
  public final SELF usingDefaultElementComparator() {
    throw new UnsupportedOperationException("custom element Comparator is not supported for CharSequence comparison");
  }

  @Override
  @CheckReturnValue
  public SELF usingComparator(Comparator<? super ACTUAL> customComparator) {
    return usingComparator(customComparator, null);
  }

  @Override
  @CheckReturnValue
  public SELF usingComparator(Comparator<? super ACTUAL> customComparator, String customComparatorDescription) {
    this.strings = new Strings(new ComparatorBasedComparisonStrategy(customComparator, customComparatorDescription));
    return super.usingComparator(customComparator, customComparatorDescription);
  }

  @Override
  @CheckReturnValue
  public SELF usingDefaultComparator() {
    this.strings = Strings.instance();
    return super.usingDefaultComparator();
  }

  @Override
  @CheckReturnValue
  public SELF inHexadecimal() {
    return super.inHexadecimal();
  }

  @Override
  @CheckReturnValue
  public SELF inBinary() {
    return super.inBinary();
  }

  /**
   * Use Unicode character representation instead of standard representation in error messages.
   * <p>
   * It can be useful when comparing UNICODE characters - many Unicode chars have duplicate characters assigned, it is
   * thus impossible to find differences from the standard error message:
   * <p>
   * With standard message:
   * <pre><code class='java'> assertThat("µµµ").contains("μμμ");
   *
   * java.lang.AssertionError:
   * Expecting:
   *   &lt;"µµµ"&gt;
   * to contain:
   *   &lt;"μμμ"&gt;</code></pre>
   * <p>
   * With Hexadecimal message:
   * <pre><code class='java'> assertThat("µµµ").inUnicode().contains("μμμ");
   *
   * java.lang.AssertionError:
   * Expecting:
   *   &lt;&bsol;u00b5&bsol;u00b5&bsol;u00b5&gt;
   * to contain:
   *   &lt;&bsol;u03bc&bsol;u03bc&bsol;u03bc&gt;</code></pre>
   *
   * @return {@code this} assertion object.
   */
  @CheckReturnValue
  public SELF inUnicode() {
    info.useUnicodeRepresentation();
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is equal to the given one, ignoring whitespace differences
   * <p>
   * Examples :
   * <pre><code class='java'> // assertions will pass
   * assertThat("Game of Thrones").isEqualToIgnoringWhitespace("Game   of   Thrones")
   *                              .isEqualToIgnoringWhitespace("  Game of   Thrones  ")
   *                              .isEqualToIgnoringWhitespace("  Game of Thrones  ")
   *                              .isEqualToIgnoringWhitespace("Gameof      Thrones")
   *                              .isEqualToIgnoringWhitespace("Game of\tThrones")
   *                              .isEqualToIgnoringWhitespace("GameofThrones");
   *
   * // assertion will fail
   * assertThat("Game of Thrones").isEqualToIgnoringWhitespace("Game OF Thrones");</code></pre>
   * <p>
   * This assertion behavior has changed in 2.8.0 to really ignore all whitespaces,
   * the old behaviour has been kept in the better named {@link #isEqualToNormalizingWhitespace(CharSequence)}.
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not equal ignoring whitespace differences to the given
   *                        one.
   */
  public SELF isEqualToIgnoringWhitespace(CharSequence expected) {
    strings.assertEqualsIgnoringWhitespace(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is not equal to the given one, ignoring whitespace differences.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot; my\tfoo bar &quot;).isNotEqualToIgnoringWhitespace(&quot;myfoo&quot;);
   * assertThat(&quot; my\tfoo&quot;).isNotEqualToIgnoringWhitespace(&quot; my bar&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;my      foo bar&quot;).isNotEqualToIgnoringWhitespace(&quot;my foo bar&quot;);
   * assertThat(&quot;  my foo bar  &quot;).isNotEqualToIgnoringWhitespace(&quot;my foo bar&quot;);
   * assertThat(&quot; my     foo bar &quot;).isNotEqualToIgnoringWhitespace(&quot;my foo bar&quot;);
   * assertThat(&quot; my\tfoo bar &quot;).isNotEqualToIgnoringWhitespace(&quot; my foo bar&quot;);
   * assertThat(&quot;my foo bar&quot;).isNotEqualToIgnoringWhitespace(&quot;   my foo bar   &quot;);
   * </code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is equal ignoring whitespace differences to the given
   *                        one.
   */
  public SELF isNotEqualToIgnoringWhitespace(CharSequence expected) {
    strings.assertNotEqualsIgnoringWhitespace(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is equal to the given one, after the whitespace
   * of both strings has been normalized.<br>
   * To be exact, the following rules are applied:
   * <ul>
   * <li>all leading and trailing whitespace of both actual and expected strings are ignored</li>
   * <li>any remaining whitespace (including non-breaking spaces), appearing within either string, is collapsed to a single space before comparison</li>
   * </ul>
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat("Game of Thrones").isEqualToNormalizingWhitespace("Game   of   Thrones")
   *                              .isEqualToNormalizingWhitespace("Game of     Thrones")
   *                              .isEqualToNormalizingWhitespace("Game     of Thrones")
   *                              .isEqualToNormalizingWhitespace("  Game of Thrones  ")
   *                              .isEqualToNormalizingWhitespace("  Game of   Thrones  ")
   *                              .isEqualToNormalizingWhitespace("Game of\tThrones")
   *                              .isEqualToNormalizingWhitespace("Game of Thrones");
   *                              .isEqualToNormalizingWhitespace("Game\u00A0of Thrones");
   *
   * // assertions will fail
   * assertThat("Game of Thrones").isEqualToNormalizingWhitespace("Game ofThrones");
   * assertThat("Game of Thrones").isEqualToNormalizingWhitespace("Gameo fThrones");
   * assertThat("Game of Thrones").isEqualToNormalizingWhitespace("Gameof Thrones");</code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not equal to the given one
   *                        after whitespace has been normalized.
   * @since 2.8.0 / 3.8.0
   */
  public SELF isEqualToNormalizingWhitespace(CharSequence expected) {
    strings.assertEqualsNormalizingWhitespace(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is not equal to the given one, after the whitespace
   * of both strings has been normalized.<br>
   * To be exact, the following rules are applied:
   * <ul>
   * <li>all leading and trailing whitespace of both actual and expected strings are ignored</li>
   * <li>any remaining whitespace (including non-breaking spaces), appearing within either string, is collapsed to a single space before comparison</li>
   * </ul>
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot; my\tfoo&quot;).isNotEqualToNormalizingWhitespace(&quot; my bar&quot;);
   * assertThat(&quot; my\tfoo bar &quot;).isNotEqualToNormalizingWhitespace(&quot; my foobar&quot;);
   *
   * // assertions will fail
   * assertThat(&quot;my      foo bar&quot;).isNotEqualToNormalizingWhitespace(&quot;my foo bar&quot;);
   * assertThat(&quot;  my foo bar  &quot;).isNotEqualToNormalizingWhitespace(&quot;my foo bar&quot;);
   * assertThat(&quot; my     foo bar &quot;).isNotEqualToNormalizingWhitespace(&quot;my foo bar&quot;);
   * assertThat(&quot; my\tfoo bar &quot;).isNotEqualToNormalizingWhitespace(&quot; my foo bar&quot;);
   * assertThat(&quot;my foo bar&quot;).isNotEqualToNormalizingWhitespace(&quot;   my foo bar   &quot;);
   * </code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is equal to the given one
   *                        after whitespace has been normalized.
   * @since 2.8.0 / 3.8.0
   */
  public SELF isNotEqualToNormalizingWhitespace(CharSequence expected) {
    strings.assertNotEqualsNormalizingWhitespace(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is equal to the given one, after the punctuation
   * of both strings has been normalized.
   * <p>
   * To be exact, the following rules are applied:
   * <ul>
   * <li>All punctuation of actual and expected strings are ignored and whitespaces (including non-breaking spaces) are normalized</li>
   * <li>Punctuation is any of the following character <b>{@code !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~}</b></li>
   * </ul>
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat("Game'of'Thrones").isEqualToNormalizingPunctuationAndWhitespace("GameofThrones")
   * assertThat("Game of Throne's").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
   * assertThat(":Game of Thrones:").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
   * assertThat(":Game-of-Thrones:").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
   * assertThat("Game of Thrones?").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
   * assertThat("Game of Thrones!!!").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
   * assertThat("Game of  {{(!)}}    Thrones!!!").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
   * assertThat("{(Game)-(of)-(Thrones)!!!}").isEqualToNormalizingPunctuationAndWhitespace("GameofThrones");
   *
   * // assertions will fail
   * assertThat("Game-of-Thrones").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones");
   * assertThat("{Game:of:Thrones}").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones");
   * assertThat("{(Game)-(of)-(Thrones)!!!}").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones");</code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not equal to the given one
   *                        after punctuation have been normalized.
   * @since 3.16.0
   */
  public SELF isEqualToNormalizingPunctuationAndWhitespace(CharSequence expected) {
    strings.assertEqualsNormalizingPunctuationAndWhitespace(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is a substring of the given one (opposite assertion of {@link #contains(CharSequence...) contains(CharSequence cs)}.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Lego&quot;).isSubstringOf(&quot;Legolas&quot;);
   * assertThat(&quot;Legolas&quot;).isSubstringOf(&quot;Legolas&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).isSubstringOf(&quot;Frod&quot;);</code></pre>
   *
   * @param sequence the sequence that is expected to contain actual.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not a substring of the given parameter.
   */
  public SELF isSubstringOf(CharSequence sequence) {
    strings.assertIsSubstringOf(info, actual, sequence);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains the given regular expression.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).containsPattern(&quot;Fr.d&quot;);
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).containsPattern(&quot;Frodod&quot;);</code></pre>
   *
   * @param regex the regular expression to find in the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the given regular expression cannot be found in the actual {@code CharSequence}.
   */
  public SELF containsPattern(CharSequence regex) {
    strings.assertContainsPattern(info, actual, regex);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains the given regular expression pattern, then accepts the given
   * {@code Consumer<Matcher>} for further verification on the matcher.
   * <p>
   * Example :
   * <pre><code class='java'> // assertion succeeds:
   * assertThat(&quot;Frodo&quot;).containsPatternSatisfying(&quot;.o(.o)&quot;, matcher -&gt; assertThat(matcher.group(1)).isEqualTo(&quot;do&quot;)); </code></pre>
   *
   * @param regex          the regular expression to find in the actual {@code CharSequence}.
   * @param matchSatisfies a consumer for further verifying the Matcher.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the given regular expression cannot be found in the actual {@code CharSequence}.
   */
  public SELF containsPatternSatisfying(CharSequence regex, Consumer<Matcher> matchSatisfies) {
    return internalContainsPatternSatisfying(Pattern.compile(regex.toString()), matchSatisfies);
  }

  /**
   * Verifies that the actual {@code CharSequence} contains the given regular expression pattern.
   * <p>
   * Example:
   * <pre><code class='java'> // assertion will pass
   * assertThat(&quot;Frodo&quot;).containsPattern(Pattern.compile(&quot;Fr.d&quot;));
   *
   * // assertion will fail
   * assertThat(&quot;Frodo&quot;).containsPattern(Pattern.compile(&quot;Frodod&quot;));</code></pre>
   *
   * @param pattern the regular expression to find in the actual {@code CharSequence}.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the given regular expression cannot be found in the actual {@code CharSequence}.
   */
  public SELF containsPattern(Pattern pattern) {
    strings.assertContainsPattern(info, actual, pattern);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} contains the given regular expression pattern, then accepts the given
   * {@code Consumer<Matcher>} for further verification on the matcher.
   * <p>
   * Example :
   * <pre><code class='java'> Pattern pattern = Pattern.compile(&quot;.o(.o)&quot;);
   *
   * // assertion succeeds:
   * assertThat(&quot;Frodo&quot;).containsPatternSatisfying(pattern, matcher -&gt; assertThat(matcher.group(1)).isEqualTo(&quot;do&quot;)); </code></pre>
   *
   * @param pattern        the regular expression to find in the actual {@code CharSequence}.
   * @param matchSatisfies a consumer for further verifying the Matcher.
   * @return {@code this} assertion object.
   * @throws NullPointerException if the given pattern is {@code null}.
   * @throws AssertionError       if the actual {@code CharSequence} is {@code null}.
   * @throws AssertionError       if the given regular expression cannot be found in the actual {@code CharSequence}.
   */
  public SELF containsPatternSatisfying(Pattern pattern, Consumer<Matcher> matchSatisfies) {
    return internalContainsPatternSatisfying(pattern, matchSatisfies);
  }

  private SELF internalContainsPatternSatisfying(Pattern pattern, Consumer<Matcher> matchSatisfies) {
    Matcher matcher = pattern.matcher(actual);
    strings.assertContainsPattern(info, actual, matcher);
    matchSatisfies.accept(matcher);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is equals to another
   * {@code CharSequence} after normalizing new line characters
   * (i.e. '\r\n' == '\n').
   * <p>
   * This assertion succeeds:
   * <pre><code class='java'> String bookName = &quot;Lord of the Rings\r\n&quot;;
   * assertThat(bookName).isEqualToNormalizingNewlines(&quot;Lord of the Rings\n&quot;);</code></pre>
   * <p>
   * Whereas this assertion fails:
   * <pre><code class='java'> String singleLine = &quot;\n&quot;;
   * assertThat(singleLine).isEqualToNormalizingNewlines(&quot;&quot;);</code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} and the given {@code CharSequence} are different
   *                        after new lines are normalized.
   * @since 2.7.0 / 3.7.0
   */
  public SELF isEqualToNormalizingNewlines(CharSequence expected) {
    strings.assertIsEqualToNormalizingNewlines(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is equal to the given one after both strings newlines (\n, \r\n) have been removed.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat("Some textWith new lines").isEqualToIgnoringNewlines("Some text\nWith new lines")
   *                                      .isEqualToIgnoringNewlines("Some text\r\nWith new lines")
   *                                      .isEqualToIgnoringNewlines("Some text\n\nWith new lines");
   *
   * assertThat("Some text\nWith new lines").isEqualToIgnoringNewlines("Some text\nWith new lines")
   *                                        .isEqualToIgnoringNewlines("Some text\r\nWith new lines")
   *                                        .isEqualToIgnoringNewlines("Some text\n\nWith new lines");
   *
   * // assertions will fail
   * assertThat("Some text\nWith new lines").isEqualToIgnoringNewlines("Some text With new lines");
   * assertThat("Some text\r\nWith new lines").isEqualToIgnoringNewlines("Some text With new lines");</code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not equal to the given one after new lines have been removed.
   */
  public SELF isEqualToIgnoringNewlines(CharSequence expected) {
    strings.assertIsEqualToIgnoringNewlines(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is a lowercase {@code CharSequence} by comparing it to
   * a lowercase {@code actual} built with {@link String#toLowerCase()}.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;lego&quot;).isLowerCase();
   * assertThat("").isLowerCase();
   * assertThat(" ").isLowerCase();
   * assertThat(".").isLowerCase();
   * assertThat("7").isLowerCase();
   * assertThat("a.7").isLowerCase();
   *
   * // assertions will fail
   * assertThat(&quot;Lego&quot;).isLowerCase();
   * assertThat(&quot;LEGO&quot;).isLowerCase();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not lowercase.
   * @see #isMixedCase()
   * @see #isUpperCase()
   */
  public SELF isLowerCase() {
    strings.assertLowerCase(info, actual);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is a mixed case {@code CharSequence}, i.e.,
   * neither uppercase nor lowercase.
   * <p>
   * If actual is empty or contains only case-independent characters, the assertion will pass.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;Capitalized&quot;).isMixedCase();
   * assertThat(&quot;camelCase&quot;).isMixedCase();
   * assertThat(&quot;rAndOMcAse1234&quot;).isMixedCase();
   * assertThat(&quot;1@3$567&quot;).isMixedCase();
   * assertThat(&quot;&quot;).isMixedCase();
   *
   * // assertions will fail
   * assertThat(&quot;I AM GROOT!&quot;).isMixedCase();
   * assertThat(&quot;please be quiet&quot;).isMixedCase();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not mixed case.
   * @see #isLowerCase()
   * @see #isUpperCase()
   * @since 3.21.0
   */
  public SELF isMixedCase() {
    strings.assertMixedCase(info, actual);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is an uppercase {@code CharSequence} by comparing it to
   * an uppercase {@code actual} built with {@link String#toUpperCase()}.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;LEGO&quot;).isUpperCase();
   * assertThat("").isUpperCase();
   * assertThat(" ").isUpperCase();
   * assertThat(".").isUpperCase();
   * assertThat("7").isUpperCase();
   * assertThat("A.7").isUpperCase();
   *
   * // assertions will fail
   * assertThat(&quot;Lego&quot;).isUpperCase();
   * assertThat(&quot;lego&quot;).isUpperCase();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not uppercase.
   * @see #isLowerCase()
   * @see #isMixedCase()
   */
  public SELF isUpperCase() {
    strings.assertUpperCase(info, actual);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is equal to the given one after they have been normalized
   * according to the {@link java.text.Normalizer.Form#NFC} form, which is a canonical decomposition followed by canonical composition.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions succeed:
   *
   * // Ä = &#92;u00C4 - Ä = &#92;u0041&#92;u0308
   * assertThat(&quot;Ä&quot;).isEqualToNormalizingUnicode(&quot;Ä&quot;);
   * assertThat(&quot;&#92;u00C4&quot;).isEqualToNormalizingUnicode(&quot;&#92;u0041&#92;u0308&quot;);
   *
   * // Amélie = u0041&#92;u006d&#92;u00e9&#92;u006c&#92;u0069&#92;u0065 - Amélie = &#92;u0041&#92;u006d&#92;u0065&#92;u0301&#92;u006c&#92;u0069&#92;u0065
   * assertThat(&quot;Amélie&quot;).isEqualToNormalizingUnicode(&quot;Amélie&quot;);
   *
   * // assertions fail:
   * assertThat(&quot;ñ&quot;).isEqualToNormalizingUnicode(&quot;n&quot;);
   * assertThat(&quot;Ä&quot;).isEqualToNormalizingUnicode(&quot;b&quot;);</code></pre>
   *
   * @param expected the given {@code CharSequence} to compare the actual {@code CharSequence} to.
   * @return {@code this} assertion object.
   * @throws AssertionError       if the actual {@code CharSequence} is not equal to the given one
   *                              after both strings have been normalized to according to the {@link java.text.Normalizer.Form#NFC} form .
   * @throws NullPointerException if the actual {@code CharSequence} is not null and the given is.
   * @since 3.19.0
   */
  public SELF isEqualToNormalizingUnicode(CharSequence expected) {
    strings.assertEqualsToNormalizingUnicode(info, actual, expected);
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is alphabetic by checking it against the {@code \p{Alpha}+} regex pattern
   * POSIX character classes (US-ASCII only).
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;lego&quot;).isAlphabetic();
   * assertThat(&quot;a&quot;).isAlphabetic();
   * assertThat(&quot;Lego&quot;).isAlphabetic();
   *
   * // assertions will fail
   * assertThat(&quot;1&quot;).isAlphabetic();
   * assertThat(&quot; &quot;).isAlphabetic();
   * assertThat(&quot;&quot;).isAlphabetic();
   * assertThat(&quot;L3go&quot;).isAlphabetic();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not alphabetic.
   * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html">java.util.regex.Pattern</a>
   */
  public SELF isAlphabetic() {
    isNotNull();
    if (!Pattern.matches("\\p{Alpha}+", actual)) throwAssertionError(shouldBeAlphabetic(actual));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is alphanumeric by checking it against the {@code \p{Alnum}+} regex pattern
   * POSIX character classes (US-ASCII only).
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;lego&quot;).isAlphanumeric();
   * assertThat(&quot;a1&quot;).isAlphanumeric();
   * assertThat(&quot;L3go&quot;).isAlphanumeric();
   *
   * // assertions will fail
   * assertThat(&quot;!&quot;).isAlphanumeric();
   * assertThat(&quot;&quot;).isAlphanumeric();
   * assertThat(&quot; &quot;).isAlphanumeric();
   * assertThat(&quot;L3go!&quot;).isAlphanumeric();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not alphanumeric.
   * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html">java.util.regex.Pattern</a>
   */
  public SELF isAlphanumeric() {
    isNotNull();
    if (!Pattern.matches("\\p{Alnum}+", actual)) throwAssertionError(shouldBeAlphanumeric(actual));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is ASCII by checking it against the {@code \p{ASCII}+} regex pattern
   * POSIX character classes (US-ASCII only).
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;lego&quot;).isASCII();
   * assertThat(&quot;a1&quot;).isASCII();
   * assertThat(&quot;L3go&quot;).isASCII();
   *
   * // assertions will fail
   * assertThat(&quot;&quot;).isASCII();
   * assertThat(&quot;♪&quot;).isASCII();
   * assertThat(&quot;⌃&quot;).isASCII();
   * assertThat(&quot;L3go123⌃00abc&quot;).isASCII();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not ASCII.
   * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html">java.util.regex.Pattern</a>
   */
  public SELF isASCII() {
    isNotNull();
    if (!Pattern.matches("\\p{ASCII}+", actual)) throwAssertionError(shouldBeASCII(actual));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is hexadecimal by checking it against the {@code \p{XDigit}+} regex pattern
   * POSIX character classes (US-ASCII only).
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;A&quot;).isHexadecimal();
   * assertThat(&quot;2&quot;).isHexadecimal();
   *
   * // assertions will fail
   * assertThat(&quot;!&quot;).isHexadecimal();
   * assertThat(&quot;&quot;).isHexadecimal();
   * assertThat(&quot; &quot;).isHexadecimal();
   * assertThat(&quot;Z&quot;).isHexadecimal();
   * assertThat(&quot;L3go!&quot;).isHexadecimal();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not hexadecimal.
   * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html">java.util.regex.Pattern</a>
   */
  public SELF isHexadecimal() {
    isNotNull();
    if (!Pattern.matches("\\p{XDigit}+", actual)) throwAssertionError(shouldBeHexadecimal(actual));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is printable by checking it against the {@code \p{Print}+} regex pattern
   * POSIX character classes (US-ASCII only).
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;2&quot;).isPrintable();
   * assertThat(&quot;a&quot;).isPrintable();
   * assertThat(&quot;~&quot;).isPrintable();
   * assertThat(&quot;&quot;).isPrintable();
   *
   * // assertions will fail
   * assertThat(&quot;\t&quot;).isPrintable();
   * assertThat(&quot;§&quot;).isPrintable();
   * assertThat(&quot;©&quot;).isPrintable();
   * assertThat(&quot;\n&quot;).isPrintable();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not printable.
   * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html">java.util.regex.Pattern</a>
   */
  public SELF isPrintable() {
    isNotNull();
    if (!Pattern.matches("\\p{Print}+", actual)) throwAssertionError(shouldBePrintable(actual));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} is visible by checking it against the {@code \p{Graph}+} regex pattern
   * POSIX character classes (US-ASCII only).
   * <p>
   * Example:
   * <pre><code class='java'> // assertions will pass
   * assertThat(&quot;2&quot;).isVisible();
   * assertThat(&quot;a&quot;).isVisible();
   * assertThat(&quot;.&quot;).isVisible();
   *
   * // assertions will fail
   * assertThat(&quot;\t&quot;).isVisible();
   * assertThat(&quot;\n&quot;).isVisible();
   * assertThat(&quot;&quot;).isVisible();
   * assertThat(&quot; &quot;).isVisible();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} is not visible.
   * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html">java.util.regex.Pattern</a>
   */
  public SELF isVisible() {
    isNotNull();
    if (!Pattern.matches("\\p{Graph}+", actual)) throwAssertionError(shouldBeVisible(actual));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not start with whitespaces (as per
   * {@link Character#isWhitespace(int)} definition), it also checks it is not null as a prerequisite.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions succeed:
   * assertThat(&quot;abc&quot;).doesNotStartWithWhitespaces();
   * assertThat(&quot;abc  &quot;).doesNotStartWithWhitespaces();
   * assertThat(&quot;abc\t\t&quot;).doesNotStartWithWhitespaces();
   * assertThat(&quot;&quot;).doesNotStartWithWhitespaces();
   *
   * // assertions fail:
   * assertThat(&quot;  abc&quot;).doesNotStartWithWhitespaces();
   * assertThat(&quot;  abc  &quot;).doesNotStartWithWhitespaces();
   * assertThat(&quot;\r\nabc&quot;).doesNotStartWithWhitespaces();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} starts with whitespace or is null.
   * @see Character#isWhitespace(int)
   * @since 3.26.0
   */
  public SELF doesNotStartWithWhitespaces() {
    isNotNull();
    actual.codePoints().boxed().findFirst().filter(Character::isWhitespace)
          .ifPresent(__ -> throwAssertionError(shouldNotStartWithWhitespaces(actual)));
    return myself;
  }

  /**
   * Verifies that the actual {@code CharSequence} does not end with whitespaces (as per
   * {@link Character#isWhitespace(int)} definition), it also checks it is not null as a prerequisite.
   * <p>
   * Example:
   * <pre><code class='java'> // assertions succeed:
   * assertThat(&quot;abc&quot;).doesNotEndWithWhitespaces();
   * assertThat(&quot;  abc&quot;).doesNotEndWithWhitespaces();
   * assertThat(&quot;\t\tabc&quot;).doesNotEndWithWhitespaces();
   * assertThat(&quot;&quot;).doesNotEndWithWhitespaces();
   *
   * // assertions fail:
   * assertThat(&quot;abc  &quot;).doesNotEndWithWhitespaces();
   * assertThat(&quot;  abc  &quot;).doesNotEndWithWhitespaces();
   * assertThat(&quot;abc\r\n&quot;).doesNotEndWithWhitespaces();</code></pre>
   *
   * @return {@code this} assertion object.
   * @throws AssertionError if the actual {@code CharSequence} ends with whitespace or is null.
   * @see Character#isWhitespace(int)
   * @since 3.26.0
   */
  public SELF doesNotEndWithWhitespaces() {
    isNotNull();
    actual.codePoints().boxed().reduce((v1, v2) -> v2).filter(Character::isWhitespace)
          .ifPresent(__ -> throwAssertionError(shouldNotEndWithWhitespaces(actual)));
    return myself;
  }

  private static boolean isBlank(CharSequence actual) {
    return isNullOrEmpty(actual) || strictlyContainsWhitespaces(actual);
  }

  private static boolean containsOnlyWhitespaces(CharSequence actual) {
    return !isNullOrEmpty(actual) && strictlyContainsWhitespaces(actual);
  }

  private static boolean containsWhitespaces(CharSequence actual) {
    return !isNullOrEmpty(actual) && containsOneOrMoreWhitespaces(actual);
  }

  private static boolean isNullOrEmpty(CharSequence actual) {
    return actual == null || actual.isEmpty();
  }

  private static boolean strictlyContainsWhitespaces(CharSequence actual) {
    return actual.chars().allMatch(Character::isWhitespace);
  }

  private static boolean containsOneOrMoreWhitespaces(CharSequence actual) {
    return actual.chars().anyMatch(Character::isWhitespace);
  }

}
